package edu.unc.ils.mrc.vocabulary;


import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import org.jgrapht.WeightedGraph;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.WeightedMultigraph;
import org.openrdf.concepts.skos.core.Concept;
import org.openrdf.elmo.ElmoModule;
import org.openrdf.elmo.sesame.SesameManager;
import org.openrdf.elmo.sesame.SesameManagerFactory;
import org.openrdf.repository.Repository;
import org.openrdf.repository.sail.SailRepository;
import org.openrdf.sail.nativerdf.NativeStore;

public class RWVocabulary extends Vocabulary
{
    Random random = new Random(System.nanoTime());
    
    double btWeight = 3;
    double ntWeight = 0;
    double rtWeight = 1;
    
    String vocabularyName;
    String vocabularyPath;
    
    SesameManager manager;
    NativeStore store;
    Repository repository;
    SesameManagerFactory factory;
    
    int numTerms = 0;
    
    Map<Integer, String> termIdToTermMap = new HashMap<Integer, String>();
    Map<String, Integer> termToTermIdMap = new HashMap<String, Integer>();
    Map<String, Set<Integer>> wordMap = new HashMap<String, Set<Integer>>();
    List<String> stopWords = new ArrayList<String>();
    
    WeightedGraph<Integer, DefaultWeightedEdge> graph = 
        new WeightedMultigraph<Integer, DefaultWeightedEdge>(DefaultWeightedEdge.class);
    
    public void setBtWeight(int btWeight) {
        this.btWeight = btWeight;
    }
    
    public void setNtWeight(int ntWeight) {
        this.ntWeight = ntWeight;
    }
    
    public void setRtWeight(int rtWeight) {
        this.rtWeight = rtWeight;
    }
    
    public void open(String path) throws Exception
    {
        this.vocabularyName = new File(path).getName();
        this.vocabularyPath = path;
        

        // Open the Sesame repository
        String indexes = "spoc,ospc";
        this.store = new NativeStore(new File(vocabularyPath), indexes);
        this.repository = new SailRepository(store);
        this.repository.initialize();
        ElmoModule module = new ElmoModule();
        this.factory = new SesameManagerFactory(module, repository);
        this.manager = factory.createElmoManager(); 
    }
    
    public void close() throws Exception
    {
        this.repository.shutDown();
        this.store.shutDown();
    }
    
    public Set<Integer> getTermIdsForWord(String word) 
    {
        Set<Integer> termIds = new HashSet<Integer>();
        if (!isStopword(word))
            termIds = wordMap.get(word.toLowerCase());
        return termIds;
    }
    
    public Integer getTermId(String term) {
        return termToTermIdMap.get(term);
    }
    
    public String getTermById(Integer termId) {
        return termIdToTermMap.get(termId);
    }
    
    public int getNextRandom(int termId) 
    {
        double total = 0.0;
        
        String term = this.getTermById(termId);
        
        // Get the edges for this termId
        Set<DefaultWeightedEdge> edges = graph.edgesOf(termId);
        double[] weights = new double[edges.size()];
        int[] terms = new int[edges.size()];
        int i=0;
        for (DefaultWeightedEdge edge: edges)
        {
            int sId = graph.getEdgeSource(edge);
            int tId = graph.getEdgeTarget(edge);
            if (sId != termId)
                terms[i] = sId;
            else
                terms[i] = tId;
            
            weights[i] = graph.getEdgeWeight(edge);
            total += weights[i];
            i++;
        }
       
        // Weighted random selection
        double choice = random.nextDouble() * total;
        
        int selected = 0;
        for (int j=0; j < i; j++) {
            double weight = weights[j];
            choice -= weight;
            if (choice < 0) {
                selected = j;
                break;
            }
        }
        return terms[selected];
    }
    
    private void addToWordIndex(int termId, String str) 
    {
        String[] words = str.split(" ");
        for (String word: words) 
        {
            if (isStopword(str))
                continue;
            
            String stemmedWord = stem(word);
            Set<Integer> termIds = wordMap.get(stemmedWord);
            if (termIds == null)
                termIds = new HashSet<Integer>();
            termIds.add(termId);
            wordMap.put(stemmedWord.toLowerCase(), termIds);
        }
    }
    
    public void init() 
    {    
        // Add a root node, since most vocabularies do not have one.
        graph.addVertex(0);
        
        // Assign a term Id to every term and build a word index
        int termId = 1;
        for (Concept concept:  manager.findAll(Concept.class))
        {
            graph.addVertex(termId);  
            String prefLabel = concept.getSkosPrefLabel();
            termIdToTermMap.put(termId, prefLabel);
            termToTermIdMap.put(prefLabel, termId);
            
            addToWordIndex(termId, prefLabel);

            Set<String> altLabels = concept.getSkosAltLabels();
            for (String altLabel: altLabels) {
                addToWordIndex(termId, altLabel);
            }
            termId++;
        }
                

        // Build the relationship matrix
        for (Concept concept:  manager.findAll(Concept.class)) 
        {
            int tId = termToTermIdMap.get(concept.getSkosPrefLabel());
            
            Set<Concept> broaders = concept.getSkosBroaders();
            if (broaders == null || broaders.size() == 0) 
            {
                DefaultWeightedEdge edge = graph.addEdge(0, tId);
                graph.setEdgeWeight(edge, btWeight);
            }
            else
            {
                /*
                for (Concept broader: broaders) {
                    int btId = termToTermIdMap.get(broader.getSkosPrefLabel());
                    DefaultWeightedEdge edge = graph.addEdge(tId, btId);
                    graph.setEdgeWeight(edge, btWeight);
                }
                */
            }
            
            Set<Concept> narrowers = concept.getSkosNarrowers();
            for (Concept narrower: narrowers) {
                int ntId = termToTermIdMap.get(narrower.getSkosPrefLabel());
                DefaultWeightedEdge edge = graph.addEdge(tId, ntId);
                graph.setEdgeWeight(edge, ntWeight);
            }
            
            Set<Concept> relateds = concept.getSkosRelated();
            for (Concept related: relateds) {
                int rtId = termToTermIdMap.get(related.getSkosPrefLabel());
                if (!graph.containsEdge(tId, rtId))
                {
                    DefaultWeightedEdge edge = graph.addEdge(tId, rtId);
                    graph.setEdgeWeight(edge, rtWeight);
                }
            }
        }
    }

    @Override
    public List<Term> lookup(String str) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<Term> lookupNonStemmed(String str) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getPrefLabel(int conceptId) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<Term> lookupWord(String str) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<Term> lookupWordStemmed(String str) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getNextRandom(int conceptId, int btWeight, int ntWeight,
            int rtWeight) throws Exception {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public boolean isRelated(String term1, String term2) throws Exception {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isBroaderNarrower(String term1, String term2)
            throws Exception {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public int getConceptId(String prefLabel) throws Exception {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public int getSize() {
        // TODO Auto-generated method stub
        return 0;
    }
}
